perm filename FREEFO.SAI[1,LES] blob
sn#002753 filedate 1973-01-25 generic text, type T, neo UTF8
BEGIN "FREEFOROL: 15 OCT 1969
" COMMENT
OPERATION
Freeforol is a text macro processor that can be used to generate form
letters and other fill-in-the-blanks text. If you type R FREEFO, it
does the following.
1) Requests via TTY the name of a TEXT FILE on the disk
containing a macro definition, as specified below.
2) Requests an ARGUMENT FILE that is a text file on the disk
containing lists of string arguments, as specified below.
A null response (carriage return only) causes the balance of the
text file to be searched for arguments.
3) Requests a set of initial string values for arguments 0-9.
Each argument is terminated by a carriage return. A null
response for any argument terminates the sequence.
4) Requests the number of COPIES to be produced for each argument
list. A null response selects 1 copy.
5) Requests an OUTPUT DEVICE and, if DSK or DTA, a file name.
A null response selects the LPT.
6) Produces the specified text.
PROCESS
The first character of the macro file is taken as a delimiter. The
text macro begins just after the delimiter and ends at the first
occurence of <delimiter><non-parameter>, where <parameter> is a digit
or letter in A-J. In between, each occurence of
<delimiter><parameter> is replaced by the current value of the
corresponding argument. "0" specifies the argument assigned in step
3,above. "1" through "9" select the first through ninth arguments
of each list that follows, while "A" through "J" select the tenth
through twentieth arguments.
The <non-parameter> mentioned above and any characters following up
to the next <delimiter> will be used as alternative separators
between arguments in the lists that follow. Any line numbers, form
feeds, or carriage returns will be ignored as separators, but line
feeds will be used. After the separator string comes a terminator
string, followed by another <delimiter>. If the terminator string
contains one or more characters, the first character only will be
used to mark the end of the argument list. If the terminator string
is null then the occurence of two separators in a row (i. e. a null
argument) will be used to terminate each list.
If the first character following the <delimiter> at the end of the
terminator string is a TAB,then all parameters except 0 will be set
to <null> before each new set of arguments is read in. Otherwise,
these parameters will retain their old values.
On the same line following the terminator string, a series of
parameter names may (or may not) be given, separated by <delimiter>s.
They are given in order beginning with parameter 0 and, if present,
will be used to prompt the initial values in step 3, above. In any
case, the argument lists begin on the next line, or at the beginning
of the argument file if one was given in step 2 above.
Thus, the general form of the source file(s) is
<delimiter>
<macro body, including parameters>
<delimiter><separator string><delimiter><terminator>
<delimiter><optional TAB><optional name of argument 0><delimiter><arg1><del>...
<argument lists>
EXAMPLES
For example, suppose the following text were used, with <TAB>
representing the horizontal tab character.
@
This is a@2 macro processor,
but it is @1.
@<TAB>@
@unused@second phrase@adjective@
fairly general<TAB> simple
rather easy to understand<TAB>n elegant
It is seen that @ is the delimiter and that the macro body ends at
the @ preceding the first <TAB>. It is also seen that <TAB> is the
separator and <LINE FEED> is the terminator for the argument lists,
so the following output would be produced.
This is a simple macro processor,
but it is fairly general.
This is an elegant macro processor,
but it is fairly easy to understand.
Alternatively, if the source text were of the form
@
<macro body>
@
<TAB>@@<names of arguments,separated by @>
<argument lists>
then the arguments would be separated by either <TAB> or <LINE FEED>
and each list would be terminated by two of these in a row (e. g. a
blank line).
FEATURES
Several features should be noted.
1) Any form feeds in the macro body are preserved, but they are
ignored in the argument lists.
2) Excess or unused arguments are ignored, so you can put comments
in the argument lists that will not show up in the output.
3) Whenever null arguments are given (two separators with nothing
between), the old value of the argument is retained. Similarly,
if the argument list is terminated before some parameters are
assigned new values, they retain their old values. Thus, if
repeating parameters are placed at the end, they need not be
restated on each list. You may defeat this feature and reset
all arguments to <null> each time by placing a TAB in front
of the name of argument 0, as discussed above.
4) If the automatic reset feature is not used, the initial values
assigned to the arguments at run time hold until they are replaced
from the lists (if ever). Note that parameter 0 is never assigned
a value from the lists and so may be used, for example, to enter a
date at run time that applies to the entire run.
5) If you wish to use just one set of arguments, you may enter them
all at run time provided that you use an argument list containing
no arguments;
DEFINE THIS="COMMENT", CR="'15", LF="'12", HT="'11", FF="'14",
ALT="'33", CRLF="CR&LF", NOFF="CR&'177&'21",
TYPE(TEXT)="OUT(TTY,""TEXT"")", TYPIN="INPUT(TTY,LINE)",
ERR(MES,NEXT)="BEGIN OUT(TTY,""MES""&CRLF); GO TO NEXT END";
DEFINE NOVA="25"; THIS IS MAXIMUM NUMBER OF STRING SUBSTITUTIONS;
DEFINE ARGS="20"; THIS IS THE MAXIMUM NUMBER OF AGRUMENTS;
DEFINE TTY="1", SOURCE="2", SINK="3", LINE="1", MARK="2", CHAR="3";
STRING ARRAY TEXT[0:NOVA]; THIS HOLDS SEGMENTS OF MACRO TEXT;
STRING ARRAY VAL[0:ARGS]; THIS HOLDS CURRENT ARGUMENT VALUES;
STRING DEV, SEP, FILE, S; LABEL SORRY, CHOOSE, REOPEN, ARF ;
INTEGER BRK, EOF, OOF, T, V, I, J, COPY, TERM, RESET;
THIS INITIALIZES I/O;
BREAKSET(LINE,LF,"I"); BREAKSET(LINE,CR&FF,"O"); BREAKSET(LINE,NULL,"N");
BREAKSET(CHAR,CR&FF,"X"); BREAKSET(CHAR,CR&FF,"O"); THIS GETS ONE CHARACTER;
BREAKSET(CHAR,NULL,"N"); BREAKSET(CHAR,NULL,"A");
OPEN(TTY,"TTY",1,1,1,100,BRK,EOF); OPEN(SOURCE,"DSK",1,2,0,4000,BRK,EOF);
CHOOSE: TYPE(TEXT FILE: ); EOF←1;
LOOKUP(SOURCE,TYPIN,EOF); IF EOF THEN ERR(FILE NOT FOUND,CHOOSE);
THIS READS AND SEGMENTS THE TEXT MACRO;
BREAKSET(MARK,INPUT(SOURCE,CHAR),"I"); BREAKSET(MARK,NULL,"N"); T←0;
WHILE T<2 OR I←TEXT[T-1]≥"0" AND I≤"9" OR I≥"A" AND I≤"J" DO
BEGIN IF T>NOVA THEN ERR(TOO MANY SUBSTITUTIONS.,SORRY);
TEXT[T]←INPUT(SOURCE,MARK); T←T+1;
END;
THIS FINDS THE SEPARATORS AND TERMINATOR;
BREAKSET(MARK,CR&FF,"O"); DEV←INPUT(SOURCE,MARK); THIS GETS TERMINATOR;
TERM←DEV; S←TEXT[T-1];
SEP←SCAN(S,MARK,BRK)& (IF TERM=0 THEN NULL ELSE TERM);
THIS GETS SEPARATORS AND TERMINATOR;
RESET←DEV←INPUT(SOURCE,LINE); THIS PICKS UP REST OF LINE;
THIS GETS ARGUMENT FILE, IF DIFFERENT;
ARF: TYPE("ARGUMENT FILE, IF ANY: "); FILE←TYPIN;
IF LENGTH(FILE)>0 THEN
BEGIN EOF←1; LOOKUP(SOURCE,FILE,EOF);
IF EOF THEN ERR(FILE NOT FOUND.,ARF);
END;
THIS INITIALIZES ARGUMENTS 0 THROUGH 9;
TYPE(INITIAL ARGUMENTS:
); I←0;
WHILE I=0 OR I<10 AND LENGTH(VAL[I-1])>0 DO
BEGIN OUT(TTY,(I+"0")&") "&
(IF LENGTH(DEV)>0 THEN SCAN(DEV,MARK,BRK) ELSE NULL)&": ");
VAL[I]←TYPIN; I←I+1;
END;
TYPE(COPIES: ); COPY←CVD(TYPIN); IF COPY<1 THEN COPY←1;
THIS GETS OUTPUT DEVICE;
REOPEN: TYPE(OUTPUT DEVICE: ); DEV←TYPIN;
IF LENGTH(DEV)=0 THEN DEV←"LPT"; OOF←1; OPEN(SINK,DEV,1,0,2,0,BRK,OOF);
IF OOF THEN ERR(DEVICE NOT AVAILABLE.,REOPEN);
IF DEV="D" OR DEV="d" THEN
BEGIN LABEL DEFILE; DEFILE:
TYPE(OUTPUT FILE NAME: ); ENTER(SINK,TYPIN,OOF);
IF OOF THEN ERR(TRY AGAIN,DEFILE)
END;
THIS SCANS ARGUMENT LISTS AND OUTPUTS TEXT;
BREAKSET(MARK,SEP,"I"); THIS SETS SCAN TO STOP ON SEPARATORS OR TERMINATOR;
WHILE NOT EOF DO
BEGIN
IF RESET=HT THEN FOR V←1 STEP 1 UNTIL ARGS DO VAL[V]←NULL;
V←0;
WHILE V=0 OR TERM>0 AND TERM ≠ BRK OR TERM=0 AND LENGTH(S)>0 DO
BEGIN IF V<ARGS THEN V←V+1;
S←INPUT(SOURCE,MARK); IF LENGTH(S)>0 THEN VAL[V]←S;
IF EOF AND TERM>0 THEN GO TO SORRY;
END;
FOR V←1 STEP 1 UNTIL COPY DO
BEGIN OUT(SINK,TEXT[0]);
FOR I←1 STEP 1 UNTIL T-2 DO OUT(SINK,VAL[IF J←TEXT[I]<"A"
THEN J-"0" ELSE J-"A"+10]&TEXT[I][2 TO INF]);
END
END;
SORRY: CLOSE(SINK); CLOSE(SOURCE)
END;